import pandas as pd
import numpy as np
df = pd.read_csv('data/car.csv')
'''
filter mean count 分别计算均价和汽车数量
先过滤出所属 Country 数超过2个的汽车，即若该汽车的 Country 在总体数据集中出现次数不超过2则剔除，再按 Country 分组计算价格均值、价格变异系数、该 Country 的汽车数量，其中变异系数的计算方法是标准差除以均值，并在结果中把变异系数重命名为 CoV 
'''
df.groupby('Country').filter(lambda x:x.shape[0]>2).groupby(
           'Country')['Price'].agg([(
           'CoV', lambda x: x.std()/x.mean()), 'mean', 'count'])

'''
按照表中位置的前三分之一、中间三分之一和后三分之一分组，统计 Price 的均值
'''
condition = ['Head']*20+['Mid']*20+['Tail']*20
df.groupby(condition)['Price'].mean()
'''
对类型 Type 分组，对 Price 和 HP 分别计算最大值和最小值，结果会产生多级索引，请用下划线把多级列索引合并为单层索引
'''
res = df.groupby('Type').agg({'Price': ['max'], 'HP': ['min']})
res.columns = res.columns.map(lambda x:'_'.join(x))
'''
对类型 Type 分组，对 HP 进行组内的 min-max 归一化。
'''
def normalize(s):
    s_min, s_max = s.min(), s.max()
    res = (s - s_min)/(s_max - s_min)
    return res
df.groupby('Type')['HP'].transform(normalize).head()
'''
对类型 Type 分组，计算 Disp. 与 HP 的相关系数
'''
df.groupby('Type')[['HP', 'Disp.']].apply(
   lambda x:np.corrcoef(x['HP'].values, x['Disp.'].values)[0,1])

'''
groupby 对象的构造方法是 my_groupby(df, group_cols)

支持单列分组与多列分组

支持带有标量广播的 my_groupby(df)[col].transform(my_func) 功能

pandas 的 transform 不能跨列计算，请支持此功能，即仍返回 Series 但 col 参数为多列

无需考虑性能与异常处理，只需实现上述功能，在给出测试样例的同时与 pandas 中的 transform 对比结果是否一致
'''
class my_groupby:
    def __init__(self, my_df, group_cols):
        self.my_df = my_df.copy()
        self.groups = my_df[group_cols].drop_duplicates()
        if isinstance(self.groups, pd.Series):
            self.groups = self.groups.to_frame()
        self.group_cols = self.groups.columns.tolist()
        self.groups = {i: self.groups[i].values.tolist(
                       ) for i in self.groups.columns}
        self.transform_col = None
    def __getitem__(self, col):
        self.pr_col = [col] if isinstance(col, str) else list(col)
        return self
    def transform(self, my_func):
        self.num = len(self.groups[self.group_cols[0]])
        L_order, L_value = np.array([]), np.array([])
        for i in range(self.num):
            group_df = self.my_df.reset_index().copy()
            for col in self.group_cols:
                group_df = group_df[group_df[col]==self.groups[col][i]]
            group_df = group_df[self.pr_col]
            if group_df.shape[1] == 1:
                group_df = group_df.iloc[:, 0]
            group_res = my_func(group_df)
            if not isinstance(group_res, pd.Series):
                group_res = pd.Series(group_res,
                                      index=group_df.index,
                                      name=group_df.name)
            L_order = np.r_[L_order, group_res.index]
            L_value = np.r_[L_value, group_res.values]
        self.res = pd.Series(pd.Series(L_value, index=L_order).sort_index(
                   ).values,index=self.my_df.reset_index(
                   ).index, name=my_func.__name__)
        return self.res
my_groupby(df, 'Type')
def f(s):
    res = (s-s.min())/(s.max()-s.min())
    return res
my_groupby(df, 'Type')['Price'].transform(f).head()
df.groupby('Type')['Price'].transform(f).head()
my_groupby(df, ['Type','Country'])['Price'].transform(f).head()
df.groupby(['Type','Country'])['Price'].transform(f).head()
my_groupby(df, 'Type')['Price'].transform(lambda x:x.mean()).head()
df.groupby('Type')['Price'].transform(lambda x:x.mean()).head()
my_groupby(df, 'Type')['Disp.', 'HP'].transform(
               lambda x: x['Disp.']/x.HP).head()